/*=====================================================================

  File:      Point3D.cs
  Summary:   A SQL Server user-defined type that represents a point in space
  Date:	     May 27, 2006

---------------------------------------------------------------------

  This file is part of the Microsoft SQL Server Code Samples.
  Copyright (C) Microsoft Corporation.  All rights reserved.

This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

======================================================= */

using System;
using System.Data.Sql;
using System.Data.SqlTypes;
using System.Diagnostics.CodeAnalysis;
using Microsoft.SqlServer.Server;
using System.Globalization;

namespace Microsoft.Samples.SqlServer.DB3D
{
    // Point3D is a SQL Server user-defined type that represents a point in 
    // space by using 3 floating-point coordinates for x, y and z.
    // Note that in this example there is no actual table using a
    // Point3D UDT, it's just used as a component of Triagle; however, it's
    // still handy for it to be a UDT so it's possible to project out and 
    // operate on the individual points that make up a triangle.

	[SqlUserDefinedType(Format.Native)]
	[Serializable]
    [SuppressMessage("Microsoft.Performance", "CA1815", 
                     Justification = "The value types used in this example are not meant to be compared directly.")]
	public struct Point3D : INullable
	{
		private double _x;
		private double _y;
		private double _z;
        private bool _isnull;

        public Point3D(double x, double y, double z) {
			_x = x;
			_y = y;
			_z = z;
            _isnull = false;
        }

        private Point3D(bool setnull) {
            _x = _y = _z = 0;
            _isnull = setnull;
        }

		public double X {
			get { return _x; }
			set { _x = value; }
		}

		public double Y {
			get { return _y; }
			set { _y = value; }
		}

		public double Z {
			get { return _z; }
			set { _z = value; }
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
		public void RotateX(double angle) {
			double newy=(double)(Math.Cos(angle) * _y + (-1 * Math.Sin(angle)) * _z);
			double newz=(double)(Math.Sin(angle) * _y + Math.Cos(angle) * _z);

			_y=newy;
			_z=newz;
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
		public void RotateY(double angle) {
			double newx=(double)(Math.Cos(angle) * _x + Math.Sin(angle) * _z);
			double newz=(double)((-1 * Math.Sin(angle)) * _x + Math.Cos(angle) * _z);
		
			_x=newx;
			_z=newz;
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
		public void RotateZ(double angle) {
			double newx=(double)(Math.Cos(angle) * _x + (-1 * Math.Sin(angle)) * _y);
			double newy=(double)(Math.Sin(angle) * _x + Math.Cos(angle) * _y);

			_x=newx;
			_y=newy;
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
		public void Translate(double x, double y, double z) {
			_x += x;
			_y += y;
			_z += z;
		}

        [SqlMethod(IsMutator = true, OnNullCall = false)]
		public void Scale(double s) {
			_x *= s;
			_y *= s;
			_z *= s;
		}

		public override string ToString() {
			return _x + "," + _y + "," + _z;
		}

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", 
            MessageId = "System.ArgumentException.#ctor(System.String)",
            Justification="Keeping the sample simple for clarity.")]
        public static Point3D Parse(SqlString s) {
			string[] values = s.Value.Split(',');
			if(3 != values.Length)
				throw new ArgumentException("Invalid format, should be x,y,z");

			// these will throw in the value cannot be converted to a number
			return new Point3D(double.Parse(values[0], CultureInfo.InvariantCulture),
                               double.Parse(values[1], CultureInfo.InvariantCulture),
                               double.Parse(values[2], CultureInfo.InvariantCulture));
		}

        public static Point3D Null {
            get {
                return new Point3D(true);
            }
        }

        public bool IsNull {
            get {
                return _isnull;
            }
        }
    }
}
